﻿//-----------------------------------------------------------------------
// <copyright file="OfficeColorPalette.cs" company="LMT">
//     Copyright (c) by La Minh Thuận.
// </copyright>
// <author>thuanla1985@hotmail.com</author>
//-----------------------------------------------------------------------
namespace LMT.Forms
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;

    /// <summary>
    /// the color plate component
    /// </summary>
    [DesignTimeVisible(false)]
    internal partial class OfficeColorPalette : UserControl
    {
        /// <summary>
        /// value of color box size
        /// </summary>
        private const int ColorBoxUnit = 12;

        /// <summary>
        /// value of Color Box Margin X
        /// </summary>
        private const int ColorBoxMarginX = 3;

        /// <summary>
        /// value of Color Margin Y
        /// </summary>
        private const int ColorMatrixX = 10;

        /// <summary>
        /// value of Color Margin Y
        /// </summary>
        private const int ColorMatrixY = 7;

        /// <summary>
        /// value of Top Box Margin 1
        /// </summary>
        private const int TopBoxMargin1 = ColorBoxUnit + 4;

        /// <summary>
        /// value of Top Box Margin 2
        /// </summary>
        private const int TopBoxMargin2 = TopBoxMargin1 + ColorBoxUnit + 4;

        /// <summary>
        /// value of Top Box Margin 3
        /// </summary>
        private const int TopBoxMargin3 = (6 * ColorBoxUnit) + TopBoxMargin2 + 8;

        /// <summary>
        /// value of color panel size
        /// </summary>
        private Size clrBoxSize = new Size(12, 12);

        /// <summary>
        /// location of selected color box
        /// </summary>
        private Point selectedBox = new Point(-1, -1);

        /// <summary>
        /// detect whether color pallete is selected
        /// </summary>
        private bool isPaletteSelected = false;

        /// <summary>
        /// the matrix color
        /// </summary>
        private Color[,] colorMatrix;

        /// <summary>
        /// the selected color
        /// </summary>
        private Color selectedColor = Color.Black;

        /// <summary>
        /// instance of chose color icon
        /// </summary>
        private Image bmpChoser;

        /// <summary>
        /// Initializes a new instance of the <see cref="OfficeColorPlate"/> class.
        /// </summary>
        public OfficeColorPalette()
        {
            SetStyle(ControlStyles.DoubleBuffer, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            this.InitializeComponent();
            this.CreateColorMatrix();

            this.bmpChoser = global::LMT.Forms.Properties.Resource.palette;
        }

        /// <summary>
        /// ColorPaletteSelectedHandler gateway handler
        /// </summary>
        /// <param name="color">value of selected color</param>
        /// <returns>enum of DialogResult</returns>
        public delegate DialogResult ColorPaletteSelectedHandler(ref Color color);

        /// <summary>
        /// Occurs when [color palette selected].
        /// </summary>
        public event ColorPaletteSelectedHandler ColorPaletteSelected;

        /// <summary>
        /// Occurs when [selected color changed].
        /// </summary>
        public event EventHandler SelectedColorChanged;

        /// <summary>
        /// Gets or sets the color of the selected.
        /// </summary>
        /// <value>The color of the selected.</value>
        public Color SelectedColor
        {
            get
            {
                return this.selectedColor;
            }

            set
            {
                this.selectedColor = value;
                this.Refresh();
                this.OnSelectedColorChanged();
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.MouseMove"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            this.selectedBox = this.GetSelectedBox(e.Location);
            Invalidate();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.MouseClick"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);

            if (this.isPaletteSelected)
            {
                if (this.ColorPaletteSelected != null)
                {
                    Color color = Color.White;
                    if (this.ColorPaletteSelected(ref color) == DialogResult.OK)
                    {
                        this.SelectedColor = color;
                    }
                }
            }

            if (this.selectedBox.X > -1 && this.selectedBox.Y > -1)
            {
                this.selectedColor = this.colorMatrix[this.selectedBox.X, this.selectedBox.Y];
                this.OnSelectedColorChanged();
            }
        }


        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.Paint"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            this.DrawColorBoxes(e.Graphics);
            this.DrawTexts(e.Graphics);
            this.DrawBorder(e.Graphics);
            this.DrawSelectedBox(e.Graphics);
            this.DrawPalette(e.Graphics);

            base.OnPaint(e);
        }

        /// <summary>
        /// Paints the background of the control.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.</param>
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            base.OnPaintBackground(e);
            this.DrawBackground(e.Graphics);
        }

        /// <summary>
        /// Gets the selected box.
        /// </summary>
        /// <param name="mouseLocation">The mouse location.</param>
        /// <returns>Point value</returns>
        private Point GetSelectedBox(Point mouseLocation)
        {
            int x = -1;
            int y = -1;
            bool isSelected = false;

            if (mouseLocation.Y >= TopBoxMargin1 && mouseLocation.Y <= TopBoxMargin1 + ColorBoxUnit)
            {
                y = 0;
            }
            else if (mouseLocation.Y >= TopBoxMargin2 && mouseLocation.Y <= TopBoxMargin2 + (5 * ColorBoxUnit))
            {
                y = ((mouseLocation.Y - TopBoxMargin2) / ColorBoxUnit) + 1;
            }
            else if (mouseLocation.Y >= TopBoxMargin3 && mouseLocation.Y <= TopBoxMargin3 + ColorBoxUnit)
            {
                y = 6;
            }
            else if (mouseLocation.Y > TopBoxMargin3 + ColorBoxUnit)
            {
                isSelected = true;
            }

            int tmp = (mouseLocation.X - ColorBoxMarginX) % (ColorBoxUnit + ColorBoxMarginX);

            if (tmp >= 0 && tmp <= ColorBoxUnit)
            {
                x = (mouseLocation.X - ColorBoxMarginX) / (ColorBoxUnit + ColorBoxMarginX);

                if (x >= 10)
                {
                    x = -1;
                }
            }

            this.isPaletteSelected = isSelected;

            return new Point(x, y);
        }

        /// <summary>
        /// Called when [selected color changed].
        /// </summary>
        private void OnSelectedColorChanged()
        {
            if (this.SelectedColorChanged != null)
            {
                this.SelectedColorChanged(this, new EventArgs());
            }
        }

        /// <summary>
        /// Draws the background.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        private void DrawBackground(Graphics graphics)
        {
            graphics.FillRectangle(
                                    new SolidBrush(Color.FromArgb(250, 250, 250)),
                                    this.ClientRectangle);
            graphics.FillRectangle(
                                    new SolidBrush(Color.FromArgb(221, 231, 238)),
                                    new Rectangle(2, 2, this.Width - 4, ColorBoxUnit));

            graphics.FillRectangle(
                                    new SolidBrush(Color.FromArgb(221, 231, 238)),
                                    new Rectangle(
                                                    2,
                                                    (5 * OfficeColorPalette.ColorBoxUnit) + OfficeColorPalette.TopBoxMargin2 + 5, 
                                                    this.Width - 4,
                                                    OfficeColorPalette.ColorBoxUnit));
        }

        /// <summary>
        /// Creates the color matrix.
        /// </summary>
        private void CreateColorMatrix()
        {
            this.colorMatrix = new Color[,] { 
					                        { this.C(255, 255, 255), this.C(210, 210, 210), this.C(186, 186, 186), this.C(154, 154, 154), 
						                        this.C(130, 130, 130), this.C(114, 114, 114),this. C(178, 014, 018) }, 
					                        { this.C(000, 000, 000), this.C(114, 114, 114), this.C(106, 106, 106), this.C(078, 078, 078), 
						                        this.C(054, 054, 054), this.C(030, 030, 030), this.C(234, 022, 030) }, 
					                        { this.C(246, 234, 210), this.C(182, 174, 166), this.C(154, 150, 142), this.C(114, 110, 106), 
						                        this.C(078, 074, 070), this.C(054, 050, 050), this.C(254, 186, 010) }, 
					                        { this.C(026, 062, 114), this.C(194, 206, 218), this.C(134, 154, 182), this.C(078, 106, 150), 
						                        this.C(018, 046, 082), this.C(010, 030, 054), this.C(255, 255, 000) }, 
					                        { this.C(082, 122, 174), this.C(210, 222, 243), this.C(166, 186, 214), this.C(122, 154, 194), 
						                        this.C(058, 086, 126), this.C(038, 054, 078), this.C(150, 214, 066) }, 
					                        { this.C(186, 070, 066), this.C(238, 206, 206), this.C(218, 158, 158), this.C(202, 114, 110), 
						                        this.C(134, 050, 046), this.C(086, 034, 030), this.C(026, 170, 066) }, 
					                        { this.C(150, 182, 086), this.C(226, 238, 210), this.C(202, 218, 170), this.C(174, 198, 126), 
						                        this.C(106, 130, 062), this.C(066, 082, 038), this.C(002, 178, 238) }, 
					                        { this.C(128, 102, 160), this.C(223, 216, 231), this.C(191, 178, 207), this.C(159, 140, 183), 
						                        this.C(096, 076, 120), this.C(064, 051, 080), this.C(000, 114, 188) }, 
					                        { this.C(075, 172, 198), this. C(210, 234, 240), this.C(165, 213, 226), this.C(120, 192, 212), 
						                        this.C(056, 129, 148), this.C(037, 086, 099), this.C(047, 054, 153) }, 
					                        { this.C(245, 157, 086), this.C(252, 230, 212), this.C(250, 206, 170), this.C(247, 181, 128), 
						                        this.C(183, 117, 064), this.C(122, 078, 043), this.C(111, 049, 152) }};
        }

        /// <summary>
        /// Cs the specified red.
        /// </summary>
        /// <param name="red">The red value.</param>
        /// <param name="green">The green value.</param>
        /// <param name="blue">The blue value.</param>
        /// <returns>value of Color</returns>
        private Color C(int red, int green, int blue)
        {
            return Color.FromArgb(red, green, blue);
        }

        /// <summary>
        /// Draws the border.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        private void DrawBorder(Graphics graphics)
        {
            graphics.DrawRectangle(
                                    Pens.LightGray,
                                    0,
                                    0,
                                    ((OfficeColorPalette.ColorBoxUnit + OfficeColorPalette.ColorBoxMarginX) * OfficeColorPalette.ColorMatrixX) + OfficeColorPalette.ColorBoxMarginX + 1,
                                    OfficeColorPalette.TopBoxMargin3 + 30);
        }

        /// <summary>
        /// Draws the texts.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        private void DrawTexts(Graphics graphics)
        {
            graphics.DrawString(
                                "Theme colors",
                                 this.Font,
                                 Brushes.Navy,
                                 1,
                                 1);

            graphics.DrawString(
                                "Standard colors",
                                 this.Font,
                                 Brushes.Navy,
                                 1,
                                 (5 * OfficeColorPalette.ColorBoxUnit) + OfficeColorPalette.TopBoxMargin2 + 4);
        }

        /// <summary>
        /// Draws the color boxes.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        private void DrawColorBoxes(Graphics graphics)
        {
            this.DrawColorBelt(graphics, 0, OfficeColorPalette.TopBoxMargin1, true);

            for (int i = 1; i < ColorMatrixY - 1; i++)
            {
                this.DrawColorBelt(graphics, i, ((i - 1) * ColorBoxUnit) + TopBoxMargin2, false);
            }

            for (int i = 0; i < ColorMatrixX; i++)
            {
                graphics.DrawRectangle(
                                        Pens.Gray,
                                        (i * (OfficeColorPalette.ColorBoxUnit + OfficeColorPalette.ColorBoxMarginX)) + OfficeColorPalette.ColorBoxMarginX,
                                        OfficeColorPalette.TopBoxMargin2,
                                        OfficeColorPalette.ColorBoxUnit,
                                        OfficeColorPalette.ColorBoxUnit * 5);
            }

            this.DrawColorBelt(graphics, OfficeColorPalette.ColorMatrixY - 1, OfficeColorPalette.TopBoxMargin3, true);
        }

        /// <summary>
        /// Draws the color belt.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        /// <param name="no">The color no.</param>
        /// <param name="offSet">The off set.</param>
        /// <param name="border">if set to <c>true</c> [border].</param>
        private void DrawColorBelt(Graphics graphics, int no, int offSet, bool border)
        {
            Point location = Point.Empty;
                                                
            for (int i = 0; i < ColorMatrixX; i++)
            {
                location.X = (i * (OfficeColorPalette.ColorBoxUnit + OfficeColorPalette.ColorBoxMarginX)) + OfficeColorPalette.ColorBoxMarginX;
                location.Y = offSet;

                this.DrawColorBox( 
                                    graphics,
                                    this.colorMatrix[i, no],
                                    location,
                                    border);
            }
        }

        /// <summary>
        /// Draws the color box.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        /// <param name="color">The color.</param>
        /// <param name="location">The location.</param>
        /// <param name="border">if set to <c>true</c> [border].</param>
        private void DrawColorBox(Graphics graphics, Color color, Point location, bool border)
        {
            graphics.FillRectangle(new SolidBrush(color), new Rectangle(location, this.clrBoxSize));

            if (border)
            {
                graphics.DrawRectangle( 
                                        Pens.LightGray,
                                        new Rectangle(location, this.clrBoxSize));
            }
        }

        /// <summary>
        /// Draws the selected box.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        private void DrawSelectedBox(Graphics graphics)
        {
            if (this.selectedBox.X > -1 && this.selectedBox.Y > -1)
            {
                Point point = new Point(0, 0);

                point.X = (this.selectedBox.X * (OfficeColorPalette.ColorBoxUnit + 3)) + 3;

                if (this.selectedBox.Y == 0)
                {
                    point.Y = TopBoxMargin1;
                }
                else if (this.selectedBox.Y == 6)
                {
                    point.Y = TopBoxMargin3;
                }
                else
                {
                    point.Y = ((this.selectedBox.Y - 1) * OfficeColorPalette.ColorBoxUnit) + OfficeColorPalette.TopBoxMargin2;
                }

                graphics.DrawRectangle(Pens.White, new Rectangle(point, this.clrBoxSize));
                graphics.DrawRectangle(
                                        Pens.OrangeRed, 
                                        point.X - 1,
                                        point.Y - 1,
                                        OfficeColorPalette.ColorBoxUnit + 2,
                                        OfficeColorPalette.ColorBoxUnit + 2);
            }
        }

        /// <summary>
        /// Draws the palette.
        /// </summary>
        /// <param name="graphics">The graphics.</param>
        private void DrawPalette(Graphics graphics)
        {
            graphics.DrawImage(this.bmpChoser, 2, TopBoxMargin3 + ColorBoxUnit + 2);

            graphics.DrawString(
                                "More colors..",
                                this.Font,
                                Brushes.Navy,
                                20,
                                OfficeColorPalette.TopBoxMargin3 + OfficeColorPalette.ColorBoxUnit + 2);

            if (this.isPaletteSelected)
            {
                graphics.DrawRectangle(
                                        Pens.Gray,
                                        2,
                                        OfficeColorPalette.TopBoxMargin3 + OfficeColorPalette.ColorBoxUnit + 2,
                                        this.Width - 5,
                                        OfficeColorPalette.ColorBoxUnit + 2);
            }
        }
    }
}